home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / DELPHI32 / COMPNENT / ISAMEXPT / NUMCTRL.PAS < prev    next >
Pascal/Delphi Source File  |  1996-04-04  |  18KB  |  688 lines

  1. unit NumCtrl;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  7.   Forms, Dialogs, StdCtrls, Menus, DsgnIntF;
  8.  
  9. { string edit component }
  10. type
  11.   TCustomStrEdit = class (TCustomEdit)
  12.   private
  13.     FAlignment: TAlignment;
  14.     FOldAlignment : TAlignment;
  15.     FTextMargin : integer;
  16.     FRightNull  : Boolean;
  17.     function CalcTextMargin : integer;
  18.     procedure CMEnter(var Message: TCMEnter); message CM_ENTER;
  19.     procedure CMExit(var Message: TCMExit);   message CM_EXIT;
  20.     procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  21.     procedure SetAlignment(Value: TAlignment);
  22.   protected
  23.     property Alignment: TAlignment read FAlignment write SetAlignment default taRightJustify;
  24.     property RightNull: Boolean read FRightNull write FRightNull default False;
  25.     procedure FormatText; dynamic;
  26.     procedure UnFormatText; dynamic;
  27.   public
  28.     constructor Create(AOwner: TComponent); override;
  29.   end;
  30.  
  31.   TStrEdit = class (TCustomStrEdit)
  32.   published
  33.     property Alignment;
  34.     property AutoSize;
  35.     property BorderStyle;
  36.     property CharCase; {KB}
  37.     property Color;
  38.     property Ctl3D;
  39.     property DragCursor;
  40.     property DragMode;
  41.     property Enabled;
  42.     property Font;
  43.     property HideSelection;
  44.     property MaxLength;
  45.     property ParentColor;
  46.     property ParentCtl3D;
  47.     property ParentFont;
  48.     property ParentShowHint;
  49.     property PopupMenu;
  50.     property ReadOnly;
  51.     property RightNull; {KB}
  52.     property ShowHint;
  53.     property TabOrder;
  54.     property Visible;
  55.     property OnChange;
  56.     property OnClick;
  57.     property OnDblClick;
  58.     property OnDragDrop;
  59.     property OnDragOver;
  60.     property OnEndDrag;
  61.     property OnEnter;
  62.     property OnExit;
  63.     property OnKeyDown;
  64.     property OnKeyPress;
  65.     property OnKeyUp;
  66.     property OnMouseDown;
  67.     property OnMouseMove;
  68.     property OnMouseUp;
  69.   end;
  70.  
  71. type
  72.   TNumericType = (ntGeneral, ntCurrency, ntPercentage);
  73.   TMaskString = string [25];
  74.  
  75. { mask component }
  76. type
  77.   TMasks = class (TPersistent)
  78.   private
  79.     FPositiveMask : TMaskString;
  80.     FNegativeMask : TMaskString;
  81.     FZeroMask : TMaskString;
  82.     FOnChange: TNotifyEvent;
  83.   protected
  84.     procedure SetPositiveMask (Value : TMaskString);
  85.     procedure SetNegativeMask (Value : TMaskString);
  86.     procedure SetZeroMask (Value : TMaskString);
  87.   public
  88.     constructor Create;
  89.     property OnChange: TNotifyEvent read FOnChange write FOnChange;
  90.   published
  91.     property PositiveMask : TMaskString read FPositiveMask write SetPositiveMask;
  92.     property NegativeMask : TMaskString read FNegativeMask write SetNegativeMask;
  93.     property ZeroMask : TMaskString read FZeroMask write SetZeroMask;
  94.   end;
  95.  
  96. { num edit component }
  97. type
  98.   TCustomNumEdit = class (TCustomStrEdit)
  99.   private
  100.     FDecimals : word;
  101.     FDigits : word;
  102.     FMasks : TMasks;
  103.     FMax : extended;
  104.     FMin : extended;
  105.     FNumericType : TNumericType;
  106.     FUseRounding : boolean;
  107.     FValue : extended;
  108.     FValidate : boolean;
  109.     procedure CMEnter(var Message: TCMEnter); message CM_ENTER;
  110.     procedure CMExit(var Message: TCMExit);   message CM_EXIT;
  111.     procedure SetDecimals(Value : word);
  112.     procedure SetDigits(Value : word);
  113.     procedure SetMasks (Mask : TMasks);
  114.     procedure SetMax(Value : extended);
  115.     procedure SetMin(Value : extended);
  116.     procedure SetNumericType(Value : TNumericType);
  117.     procedure SetValue(Value : extended);
  118.     procedure SetValidate(Value : boolean);
  119.   protected
  120.     procedure FormatText; dynamic;
  121.     procedure KeyPress(var Key: Char); override;
  122.     procedure UnFormatText; dynamic;
  123.     property Decimals : word read FDecimals write SetDecimals;
  124.     property Digits : word read FDigits write SetDigits;
  125.     property Masks : TMasks read FMasks write SetMasks;
  126.     property Max : extended read FMax write SetMax;
  127.     property Min : extended read FMin write SetMin;
  128.     property NumericType : TNumericType read FNumericType write SetNumericType default ntCurrency;
  129.     property UseRounding : boolean read FUseRounding write FUseRounding;
  130.     property Value : extended read FValue write SetValue;
  131.     property Validate : boolean read FValidate write SetValidate;
  132.   public
  133.     constructor Create(AOwner: TComponent); override;
  134.     destructor Destroy; override;
  135.     function AsDouble : double; dynamic;
  136.     function AsInteger : integer; dynamic;
  137.     function AsLongint : longint; dynamic;
  138.     function AsReal : real; dynamic;
  139.     function AsString : string; dynamic;
  140.     procedure MaskChanged ( Sender : TObject );
  141.     function Valid ( Value : extended ) : boolean; dynamic;
  142.   end;
  143.  
  144.   TNumEdit = class (TCustomNumEdit)
  145.   published
  146.     property AutoSize;
  147.     property BorderStyle;
  148.     property Color;
  149.     property Ctl3D;
  150.     property Decimals;
  151.     property Digits;
  152.     property DragCursor;
  153.     property DragMode;
  154.     property Enabled;
  155.     property Font;
  156.     property HideSelection;
  157.     property Masks;
  158.     property Max;
  159.     property Min;
  160.     property NumericType;
  161.     property ParentColor;
  162.     property ParentCtl3D;
  163.     property ParentFont;
  164.     property ParentShowHint;
  165.     property PopupMenu;
  166.     property ReadOnly;
  167.     property ShowHint;
  168.     property TabOrder;
  169.     property UseRounding;
  170.     property Value;
  171.     property Validate;
  172.     property Visible;
  173.     property OnChange;
  174.     property OnClick;
  175.     property OnDblClick;
  176.     property OnDragDrop;
  177.     property OnDragOver;
  178.     property OnEndDrag;
  179.     property OnEnter;
  180.     property OnExit;
  181.     property OnKeyDown;
  182.     property OnKeyPress;
  183.     property OnKeyUp;
  184.     property OnMouseDown;
  185.     property OnMouseMove;
  186.     property OnMouseUp;
  187.   end;
  188.  
  189. implementation
  190.  
  191. type
  192.   TSetOfChar = set of char;
  193. var
  194.   OldMaxLength : integer;
  195.  
  196. {========================================================================}
  197. { support routines                                                       }
  198. {========================================================================}
  199.  
  200. function Power ( X, Y : integer ) : real;
  201. begin
  202.   Result := exp ( ln ( X ) * Y );
  203. end;
  204.  
  205. function StripChars ( const Text : string; ValidChars : TSetOfChar ) : string;
  206. var
  207.   S : string;
  208.   i : integer;
  209.   Negative : boolean;
  210. Begin
  211.   Negative := false;
  212.   if (Text [ 1 ] = '-') or (Text [length (Text)] = '-' ) then
  213.     Negative := true;
  214.   S := '';
  215.   for i := 1 to length ( Text ) do
  216.     if Text [ i ] in ValidChars then
  217.       S := S + Text [ i ];
  218.   if Negative then
  219.     Result := '-' + S
  220.   else
  221.     Result := S;
  222. End;
  223.  
  224. {========================================================================}
  225. { Custom String Edit                                                     }
  226. {========================================================================}
  227.  
  228. constructor TCustomStrEdit.Create(AOwner: TComponent);
  229. begin
  230.   inherited Create(AOwner);
  231.   FAlignment := taLeftJustify;
  232.   FTextMargin := CalcTextMargin;
  233. end;
  234.  
  235. function TCustomStrEdit.CalcTextMargin : integer;
  236. {borrowed from TDBEdit}
  237. {calculates a pixel offset from the edge of the control to the text(a margin)}
  238. {used in the paint routine}
  239. var
  240.   DC: HDC;
  241.   SaveFont: HFont;
  242.   I: Integer;
  243.   SysMetrics, Metrics: TTextMetric;
  244. begin
  245.   DC := GetDC(0);
  246.   GetTextMetrics(DC, SysMetrics);
  247.   SaveFont := SelectObject(DC, Font.Handle);
  248.   GetTextMetrics(DC, Metrics);
  249.   SelectObject(DC, SaveFont);
  250.   ReleaseDC(0, DC);
  251.   I := SysMetrics.tmHeight;
  252.   if I > Metrics.tmHeight then
  253.     I := Metrics.tmHeight;
  254.   Result := I div 4;
  255. end;
  256.  
  257. procedure TCustomStrEdit.SetAlignment(Value: TAlignment);
  258. begin
  259.   if FAlignment <> Value then
  260.     begin
  261.     FAlignment := Value;
  262.     Invalidate;
  263.     end;
  264. end;
  265.  
  266. procedure TCustomStrEdit.CMEnter(var Message: TCMEnter);
  267. begin
  268.   if FRightNull then UnformatText;
  269.   inherited;
  270.   FOldAlignment := FAlignment;
  271.   Alignment := taLeftJustify;
  272. end;
  273.  
  274. procedure TCustomStrEdit.CMExit(var Message: TCMExit);
  275. begin
  276.   if FRightNull then FormatText;
  277.   inherited;
  278.   Alignment := FOldAlignment;
  279. end;
  280.  
  281. Procedure TCustomStrEdit.UnformatText;
  282. begin
  283.   Text := StripChars ( Text, [ '0'..'9', DecimalSeparator, ThousandSeparator ] );
  284. end;
  285.  
  286. procedure TCustomStrEdit.FormatText;
  287. var Txt: String;
  288. begin
  289.   Txt:= Text;
  290.   while Length(Txt) < MaxLength do Txt:= '0'+Txt;
  291.   Text:= Txt;
  292. end;
  293.  
  294. procedure TCustomStrEdit.WMPaint(var Message: TWMPaint);
  295. {borrowed from TDBEdit}
  296. {paints the text in the appropriate position}
  297. var
  298.   Width, Indent, Left, I: Integer;
  299.   R: TRect;
  300.   DC: HDC;
  301.   PS: TPaintStruct;
  302.   S: string;
  303.   Canvas: TControlCanvas;
  304. begin
  305.   {let the existing code handle left justify}
  306.   if (FAlignment = taLeftJustify) then
  307.     begin
  308.     inherited;
  309.     Exit;
  310.     end;
  311.  
  312.   try
  313.     Canvas := TControlCanvas.Create;
  314.     Canvas.Control := Self;
  315.     DC := Message.DC;
  316.     if DC = 0 then
  317.       DC := BeginPaint(Handle, PS);
  318.     Canvas.Handle := DC;
  319.  
  320.     Canvas.Font := Font;
  321.     with Canvas do
  322.       begin
  323.       R := ClientRect;
  324.       if (BorderStyle = bsSingle) then
  325.         begin
  326.         Brush.Color := clWindowFrame;
  327.         FrameRect(R);
  328.         InflateRect(R, -1, -1);
  329.         end;
  330.       Brush.Color := Color;
  331.       S := Text;
  332.       Width := TextWidth(S);
  333.       if BorderStyle = bsNone then
  334.         Indent := 0
  335.       else
  336.         Indent := FTextMargin;
  337.       if FAlignment = taRightJustify then
  338.         Left := R.Right - Width - Indent
  339.       else
  340.         Left := (R.Left + R.Right - Width) div 2;
  341.       TextRect(R, Left, Indent, S);
  342.       end;
  343.   finally
  344.     Canvas.Handle := 0;
  345.     if Message.DC = 0 then
  346.       EndPaint(Handle, PS);
  347.   end;{try}
  348. end;
  349. {========================================================================}
  350. { Masks object                                                           }
  351. {========================================================================}
  352.  
  353. constructor TMasks.Create;
  354. begin
  355.   inherited Create;
  356.   FPositiveMask := '#.##0';
  357.   FNegativeMask := '';
  358.   FZeroMask := '';
  359. end;
  360.  
  361. procedure TMasks.SetPositiveMask (Value : TMaskString);
  362. begin
  363.   if FPositiveMask <> Value then
  364.     begin
  365.     FPositiveMask := Value;
  366.     OnChange(Self);
  367.     end;
  368. end;
  369.  
  370. procedure TMasks.SetNegativeMask (Value : TMaskString);
  371. begin
  372.   if FNegativeMask <> Value then
  373.     begin
  374.     FNegativeMask := Value;
  375.     OnChange(Self);
  376.     end;
  377. end;
  378.  
  379. procedure TMasks.SetZeroMask (Value : TMaskString);
  380. begin
  381.   if FZeroMask <> Value then
  382.     begin
  383.     FZeroMask := Value;
  384.     OnChange(Self);
  385.     end;
  386. end;
  387.  
  388. {========================================================================}
  389. { Custom Numeric Edit                                                    }
  390. {========================================================================}
  391.  
  392. constructor TCustomNumEdit.Create(AOwner: TComponent);
  393. begin
  394.   inherited Create(AOwner);
  395.   Width := 85;
  396.   FAlignment := taRightJustify;
  397.   FNumericType := ntCurrency;
  398.   FDigits := 12;
  399.   FDecimals := 2;
  400.   AutoSelect := true;
  401.   FMax := 0.0;
  402.   FMin := 0.0;
  403.   FValidate := false;
  404.   FValue := 0.0;
  405.   FormatText;
  406.   FTextMargin := CalcTextMargin;
  407.   FUseRounding := true;
  408.   FMasks := TMasks.Create;
  409.   FMasks.OnChange := MaskChanged;
  410.   DecimalSeparator := '.';
  411.   ThousandSeparator := ',';
  412. end;
  413.  
  414. destructor TCustomNumEdit.Destroy;
  415. begin
  416.   FMasks.Free;
  417.   inherited Destroy;
  418. end;
  419.  
  420. function TCustomNumEdit.AsInteger : integer;
  421. const
  422.   MaxInteger : integer = 32767;
  423.   MinInteger : integer = -32768;
  424. begin
  425.   Result := 0;
  426.   if (FValue < MaxInteger) and  (FValue > MinInteger) then
  427.     if FUseRounding then
  428.       Result := round ( FValue )
  429.     else
  430.       Result := trunc ( FValue );
  431. end;
  432.  
  433. function TCustomNumEdit.AsLongint : longint;
  434. const
  435.   MaxLongint : longint = 2147483647;
  436.   MinLongint : longint = -2147483647;
  437. begin
  438.   Result := 0;
  439.   if (FValue < MaxLongint ) and  (FValue > MinLongint) then
  440.     if FUseRounding then
  441.       Result := round ( FValue )
  442.     else
  443.       Result := trunc ( FValue );
  444. end;
  445.  
  446. function TCustomNumEdit.AsReal : real;
  447. const
  448.   MaxReal : real = 1.7E38;
  449.   MinReal : real = -1.7E38;
  450. begin
  451.   Result := 0;
  452.   if (FValue < MaxReal) and  (FValue > MinReal) then
  453.      Result := FValue;
  454. end;
  455.  
  456. function TCustomNumEdit.AsDouble : double;
  457. const
  458.   MaxDouble : double = 1.7E308;
  459.   MinDouble : double = -1.7E308;
  460. begin
  461.   Result := 0;
  462.   if (FValue < MaxDouble) and  (FValue > MinDouble) then
  463.      Result := round ( FValue );
  464. end;
  465.  
  466. function TCustomNumEdit.AsString : string;
  467. const
  468.   ValidChars = [ '0'..'9', ',', '.' ];
  469. begin
  470.   Result := StripChars ( Text, ValidChars );
  471.   if Value < 0 then
  472.     Result := '-' + Result;
  473. end;
  474.  
  475. procedure TCustomNumEdit.SetMasks (Mask : TMasks);
  476. begin
  477.   if fMasks <> Mask then
  478.     begin
  479.     fMasks := Masks;
  480.     Invalidate;
  481.     end;
  482. end;
  483.  
  484. procedure TCustomNumEdit.SetMin(Value : extended);
  485. begin
  486.   if FMin <> Value then
  487.     begin
  488.     FMin := Value;
  489.     if FValue < FMin then
  490.       FValue := FMin;
  491.     end;
  492. end;
  493.  
  494. procedure TCustomNumEdit.SetMax(Value : extended);
  495. begin
  496.   if FMax <> Value then
  497.     begin
  498.     FMax := Value;
  499.     if FValue > FMax then
  500.       FValue := FMax;
  501.     end;
  502. end;
  503.  
  504. procedure TCustomNumEdit.SetValue(Value : extended);
  505. begin
  506.   if ( FValue <> Value ) and ( Valid ( Value ) ) then
  507.     begin
  508.     FValue := Value;
  509.     FormatText;
  510.     end;
  511. end;
  512.  
  513. procedure TCustomNumEdit.SetDigits(Value : word);
  514. begin
  515.   if FDigits <> Value then
  516.     begin
  517.     FDigits := Value;
  518.     FormatText;
  519.     end;
  520. end;
  521.  
  522. procedure TCustomNumEdit.SetDecimals (Value : word);
  523. var NStr: TMaskString;
  524.     i   : Integer;
  525. begin
  526.   if FDecimals <> Value then begin
  527.     FDecimals := Value;
  528.     FormatText;
  529.     if csDesigning in ComponentState then begin
  530.       NStr:= '';
  531.       i:= 0;
  532.       if FDecimals > Digits then Digits:= Decimals + 1;
  533.       While i < Digits - Decimals - 1 do begin
  534.         NStr:= NStr + '#';
  535.         Inc(i);
  536.       end;
  537.       NStr:= NStr + '0';
  538.       if (Decimals > 0) then begin
  539.         NStr:= NStr + '.';
  540.         i:= 0;
  541.         While i < Decimals - 1 do begin
  542.           NStr:= NStr + '#';
  543.           inc(i);
  544.         end;
  545.         NStr:= NStr + '0';
  546.       end;
  547.       Masks.PositiveMask:= NStr;
  548.     end;
  549.   end;
  550. end;
  551.  
  552. procedure TCustomNumEdit.SetNumericType(Value: TNumericType);
  553. begin
  554.   if FNumericType <> Value then
  555.     begin
  556.     FNumericType := Value;
  557.     FormatText;
  558.     end;
  559. end;
  560.  
  561. procedure TCustomNumEdit.SetValidate(Value : boolean);
  562. begin
  563.   if FValidate <> Value then
  564.     begin
  565.     FValidate:= Value;
  566.     if FValidate and (( FValue < FMin ) or ( FValue > FMax )) then
  567.       begin
  568.       FValue := FMin;
  569.       FormatText;
  570.       end;
  571.     end;
  572. end;
  573.  
  574. function TCustomNumEdit.Valid ( Value : extended ) : boolean;
  575. var
  576.   S : string [80];
  577. begin
  578.   Result := true;
  579.   if Validate and (( Value < FMin ) or ( Value > FMax )) then
  580.     begin
  581.     FmtStr( S, 'Der eingegebene Wert mu▀ zwischen %g und %g liegen', [FMin, FMax]);
  582.     MessageDlg(S,mtError, [mbOk], 0);
  583.     Result := false;
  584.     end;
  585. end;
  586.  
  587. procedure TCustomNumEdit.KeyPress(var Key: Char);
  588. begin
  589.   {only allow numerics, commas and one period}
  590.   if (Key = DecimalSeparator) and (pos (DecimalSeparator, Text) = 0) then
  591.     begin
  592.     inherited KeyPress(Key);
  593.     MaxLength := MaxLength + 1;
  594.     end
  595.   else
  596.   if ( Key = '-' ) and ( pos ( '-', Text ) = 0 ) then
  597.     begin
  598.     inherited KeyPress(Key);
  599.     MaxLength := MaxLength + 1;
  600.     end
  601.   else
  602.   if Key in [ '0'..'9', ThousandSeparator, #8 ] then
  603.     inherited KeyPress(Key)
  604.   else
  605.     Key := #0;
  606. end;
  607.  
  608. procedure TCustomNumEdit.CMEnter(var Message: TCMEnter);
  609. begin
  610.   {strip the mask and left justify the field}
  611.   UnFormatText;
  612.   OldMaxLength := MaxLength;
  613.   MaxLength := FDigits;
  614.   inherited;
  615. end;
  616.  
  617. procedure TCustomNumEdit.CMExit(var Message: TCMExit);
  618. var
  619.   S : string [80];
  620.   X : extended;
  621. begin
  622.   {format the string with the mask when leaving the field}
  623.   MaxLength := OldMaxLength;
  624.   S := StripChars (Text, [ '0'..'9', DecimalSeparator ]); {remove all literal characters}
  625.   if S = '' then
  626.     X := 0.0
  627.   else
  628.     X := StrToFloat ( S );
  629.   if Valid ( X ) then
  630.     begin
  631.     if FNumericType = ntPercentage then
  632.       FValue := X / 100
  633.     else
  634.       FValue := X;
  635.     FormatText;
  636.     inherited;
  637.     end
  638.   else
  639.     begin
  640.     SelectAll;
  641.     SetFocus;
  642.     end;
  643. end;
  644.  
  645. procedure TCustomNumEdit.FormatText;
  646. var
  647.   X : extended;
  648.   Multiplier : real;
  649. begin
  650.   {round the number appropriately}
  651.   try
  652.     Multiplier := Power ( 10, Decimals );
  653.     if FNumericType = ntPercentage then
  654.       X := FValue * 100
  655.     else
  656.       X := FValue;
  657.     if UseRounding then
  658.       X := round ( X * Multiplier ) / Multiplier
  659.     else
  660.       X := trunc ( X * Multiplier ) / Multiplier;
  661.   except
  662.     on ERangeError do
  663.       X := FValue; {will cause rounding in the FloatToStr function}
  664.   end;
  665.  
  666.   {format the number}
  667.   case FNumericType of
  668.     ntCurrency   : Text := FloatToStrF ( X, ffCurrency, FDigits, FDecimals);
  669.     ntPercentage : Text := FloatToStrF ( X, ffFixed, FDigits, FDecimals) + '%';
  670.     ntGeneral    : with Masks do
  671.                      Text := FormatFloat( PositiveMask+';'+NegativeMask+';'+ZeroMask, X);
  672.   end;
  673. end;
  674.  
  675. procedure TCustomNumEdit.MaskChanged ( Sender : TObject );
  676. begin
  677.   FormatText;
  678. end;
  679.  
  680. procedure TCustomNumEdit.UnFormatText;
  681. Begin
  682.   Text := StripChars ( Text, [ '0'..'9', DecimalSeparator, ThousandSeparator ] );
  683.   if Value < 0 then
  684.     Text := '-' + Text;
  685. End;
  686.  
  687. End.
  688.